
#include "hook.h"
#include "udp.h"

#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <stdint.h>
#include <string.h>

#include "ip.h"
#include "dpdk.h"

static uint8_t dpdk_flow_data_back = 0;

uint16_t init_dpdk(pSocketData d,char eal[]);

uint16_t dpdk_recv_msg(p_socket_io sock, char* buf, uint16_t* size);
uint16_t dpdk_send_msg(p_socket_io sock, char* buf, uint16_t* size);
uint16_t dpdk_check_data(char* buf, uint16_t size);
uint16_t dpdk_fill_data(char* buf, uint16_t size);
void dpdk_clean(pSocketData d);

// 默认端口配置
static const struct rte_eth_conf port_conf_default = {
#if RTE_VERSION < RTE_VERSION_NUM(DPDK_VERSION_1, DPDK_VERSION_2, DPDK_VERSION_3, DPDK_VERSION_4)
    .rxmode = {
        .max_rx_pkt_len = RTE_ETHER_MAX_LEN,
    }
#endif
};

uint16_t init_dpdk(pSocketData d, char p[])
{
	uint16_t k;
	struct rte_eth_dev_info dev_info;
	const int num_rx_queues = 1;
	const int num_tx_queues = 1;
	uint16_t  rx_queue_id = 0;
	uint16_t  tx_queue_id = 0;
	uint16_t nb_rxd = 0;
	uint16_t nb_txd = 0;
	int i;

	d->buf_size = 65535;
	d->buf_count = 8192;

	int p_count = 128;
	int p_max_len = 64;
	d->dpdk_argv = (char**)malloc(sizeof(char*)*128);
	for (i = 0; i < p_count; i++) {
		d->dpdk_argv[i] = (char*)malloc(sizeof(char)*p_max_len);
		memset(d->dpdk_argv[i], 0, p_max_len);
	}
	char* split=" ";
	printf("EAL:%s\n", p);
	char* temp = strtok(p, split);
	i = 0;
	while (temp && i < p_count) {
		k = strlen(temp);
		k = (k > p_max_len ? p_max_len : k);
		strncpy(d->dpdk_argv[i++], temp, k);
		temp = strtok(NULL, split);
	}
	d->dpdk_argc = i;

	if (rte_eal_init(d->dpdk_argc, d->dpdk_argv) < 0) {
		printf("Error with EAL initialization\n");
		return ERR_SOCKET_OPT_O;
	}

	d->mpool = rte_pktmbuf_pool_create("mbuf_pool", d->buf_count, 0, 0, d->buf_size, rte_socket_id());
	if (d->mpool == NULL) {
		printf("Could not create mbuf pool\n");
		return ERR_SOCKET_OPT_O;
	}

	uint16_t nb_sys_ports = rte_eth_dev_count_avail();
	if (nb_sys_ports == 0) {
		printf("No Ethernet ports available\n");
		return ERR_SOCKET_OPT_O;
	}
	
	// 目前仅支持单个port
	for (k = 0; k < nb_sys_ports && k < 1; k++) {
		rte_eth_dev_info_get(k, &dev_info);
		struct rte_eth_conf port_conf = port_conf_default;
		rte_eth_dev_configure(k, num_rx_queues, num_tx_queues, &port_conf);
		if (rte_eth_rx_queue_setup(k, rx_queue_id, nb_rxd, rte_eth_dev_socket_id(k), NULL, d->mpool) < 0) {
			printf("Failed to set up RX queue\n");
			return ERR_SOCKET_OPT_O;
		}

		struct rte_eth_txconf txq_conf = dev_info.default_txconf;
		txq_conf.offloads = port_conf.rxmode.offloads;
		if (rte_eth_tx_queue_setup(k, tx_queue_id, nb_txd, rte_eth_dev_socket_id(k), &txq_conf) < 0) {
			printf("Failed to set up TX queue\n");
			return ERR_SOCKET_OPT_O;
		}
		if (rte_eth_dev_start(k) < 0) {
			printf("Failed to start Ethernet device\n");
			return ERR_SOCKET_OPT_O;
		}

		rte_eth_promiscuous_enable(k); 

		rte_eth_macaddr_get(k, (struct rte_ether_addr *)(d->src_mac));
		d->portid = k;
	}
	return NO_ERR;
}

uint16_t init_flow(void* arg) 
{
	p_main_flow mflow = (p_main_flow)(arg);
	if (!mflow) {
		printf("parameter err\n");
		return ERR_SOCKET_OPT_O;
	}
	mflow->m_socket_io.m_socket_data = (void*)malloc(sizeof(SocketData));
	pSocketData d = (pSocketData)(mflow->m_socket_io.m_socket_data);
	if (!d) {
		printf("socket data uninitialized\b");
		return ERR_SOCKET_OPT_O;
	}

	if (init_dpdk(d, mflow->plugin_parameter) != NO_ERR) {
		printf("port init failed\n");
		return ERR_SOCKET_OPT_O;
	}
	rte_eth_macaddr_get(d->portid, (struct rte_ether_addr *)mflow->m_socket_io.r_local_addr.sll_addr);

	d->send_table_size = 1;
	d->recv_table_size = 1;
	d->mtable_send = (struct rte_mbuf **)malloc(d->send_table_size);
	d->mtable_recv = (struct rte_mbuf **)malloc(d->recv_table_size);
	for (int i = 0;i < d->send_table_size; i++) {
		d->mtable_send[i] = rte_pktmbuf_alloc(d->mpool);
		if (d->mtable_send[i] == NULL) {
			printf("Failed to allocate mbuf for send\n");
			return ERR_SOCKET_OPT_O;
		}
		d->mtable_send[i]->pkt_len = mflow->m_data_size;
		d->mtable_send[i]->data_len = mflow->m_data_size;
		rte_pktmbuf_mtod(d->mtable_send[i], char*);
		udp_fill_data(mflow, rte_pktmbuf_mtod(d->mtable_send[i], char*), mflow->m_data_size);
	}
	for (int i = 0;i < d->recv_table_size; i++) {
		d->mtable_recv[i] = rte_pktmbuf_alloc(d->mpool);
		if (d->mtable_recv[i] == NULL) {
			printf("Failed to allocate mbuf for recv\n");
			return ERR_SOCKET_OPT_O;
		}
		d->mtable_recv[i]->pkt_len = mflow->m_data_size;
		d->mtable_recv[i]->data_len = mflow->m_data_size;
	}

	mflow->m_input = dpdk_send_msg;
	mflow->m_output = dpdk_recv_msg;
	mflow->m_fill_data = dpdk_fill_data;
	mflow->m_check_data = dpdk_check_data;

	if (SOCKET_FLOW_TYPE_SERVER == mflow->m_socket_io.flow_type && \
			                        mflow->m_flow_data_direct == flow_data_back) {
		dpdk_flow_data_back = 1;
	}

	init_sockaddr_in_2(&(mflow->m_socket_io.local_addr), mflow->m_flow_state.sstate.local_ip, mflow->m_flow_state.sstate.local_port, 1);
	init_sockaddr_in_2(&(mflow->m_socket_io.remote_addr), mflow->m_flow_state.sstate.remote_ip, mflow->m_flow_state.sstate.remote_port, 1);

	d->local_ip = inet_addr(mflow->m_flow_state.sstate.local_ip);
	rte_memcpy(d->local_mac, mflow->m_socket_io.r_local_addr.sll_addr, RTE_ETHER_ADDR_LEN);

	return NO_ERR;
}

void clean_flow(void* arg) 
{
	p_main_flow mflow = (p_main_flow)(arg);
	if (!mflow) {
		printf("parameter err\n");
		return ;
	}
	pSocketData d = (pSocketData)(mflow->m_socket_io.m_socket_data);
	if (!d) {
		printf("socket data uninitialized\b");
		return ;
	}
	dpdk_clean(d);

}

void dpdk_clean(pSocketData d)
{
	rte_eth_dev_stop(d->portid);
	rte_eth_dev_close(d->portid);
}

uint16_t dpdk_fill_data(char* buf, uint16_t size) {
	return NO_ERR;
}

uint16_t dpdk_check_data(char* buf, uint16_t size) {
	return NO_ERR;
}

uint16_t dpdk_send_msg(p_socket_io sock, char* buf, uint16_t* size)
{
	int ret, i;
	uint16_t sum = 0;
	pSocketData d = (pSocketData)(sock->m_socket_data);
	if (size) {
		*size = 0;
	}
	if (!d) return 0;
	if (dpdk_flow_data_back) {
		struct rte_ether_hdr *eth;
		struct rte_ipv4_hdr  *iph;
		struct rte_udp_hdr   *udph;
		char   temp_mac[RTE_ETHER_ADDR_LEN];
		rte_be32_t temp_ip;
		uint16_t temp_port;
		for (i = 0; i < d->recv_pkt_size; i++) {
			eth = rte_pktmbuf_mtod(d->mtable_recv[i], struct rte_ether_hdr*);
#if RTE_VERSION == RTE_VERSION_NUM(19, 11, 0, 0)
			rte_memcpy(temp_mac, eth->s_addr.addr_bytes, RTE_ETHER_ADDR_LEN);
			rte_memcpy(eth->s_addr.addr_bytes, eth->d_addr.addr_bytes, RTE_ETHER_ADDR_LEN);
			rte_memcpy(eth->d_addr.addr_bytes, temp_mac, RTE_ETHER_ADDR_LEN);
#else
			rte_memcpy(temp_mac, eth->src_addr.addr_bytes, RTE_ETHER_ADDR_LEN);
			rte_memcpy(eth->src_addr.addr_bytes, eth->dst_addr.addr_bytes, RTE_ETHER_ADDR_LEN);
			rte_memcpy(eth->dst_addr.addr_bytes, temp_mac, RTE_ETHER_ADDR_LEN);
#endif
			if (eth->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) {
				iph = rte_pktmbuf_mtod_offset(d->mtable_recv[i], struct rte_ipv4_hdr*, sizeof(struct rte_ether_hdr));
				iph->time_to_live = 64;
				temp_ip = iph->src_addr;
				iph->src_addr = iph->dst_addr;
				iph->dst_addr = temp_ip;
				iph->hdr_checksum = 0;
				iph->hdr_checksum = rte_ipv4_cksum(iph);
			} else {
				continue;
			}
			if (iph->next_proto_id == IPPROTO_UDP) {
				udph = (struct rte_udp_hdr*)(iph+1);
				temp_port = udph->src_port;
				udph->src_port = udph->dst_port;
				udph->dst_port = temp_port;
				udph->dgram_cksum = 0;
				udph->dgram_cksum = rte_ipv4_udptcp_cksum(iph, udph);
				iph->hdr_checksum = 0;
				iph->hdr_checksum = rte_ipv4_cksum(iph);
			}
		}

		ret = rte_eth_tx_burst(d->portid, 0, d->mtable_recv, d->recv_pkt_size);
	} else {
		ret = rte_eth_tx_burst(d->portid, 0, d->mtable_send, d->send_table_size);
	}
	if (ret <= 0) {
		return 0;
	}
	for (i = 0; i < ret; i++) {
		sum += d->mtable_send[i]->data_len;
	}
	d->send_pkt_size = ret;
	if (size) {
		*size = sum;
	}
	return NO_ERR;
}
uint16_t dpdk_recv_msg(p_socket_io sock, char* buf, uint16_t* size)
{
	int ret, i;
	uint16_t sum = 0;
	pSocketData d = (pSocketData)(sock->m_socket_data);
	if (size) {
		*size = 0;
	}
	if (!d) return 0;
	ret = 0;
	do {
		ret = rte_eth_rx_burst(d->portid, 0, d->mtable_recv, d->recv_table_size);
		d->recv_pkt_size = ret;
		sum = 0;
		for (i = 0; i < ret; i++) {
			sum += handle_ip(d->mtable_recv[i], d);
		}
		if (sum == 0) ret = 0;
	} while(ret <= 0);
	if (size) {
		*size = sum;
	}
	return NO_ERR;
}



